ccR.Spell = {
	Can = {
		Attack = function(spell, unit, ignoreIsUsable) return ccR.Spell.CanAttack(spell, unit, ignoreIsUsable) end,
	},
	CanAttack = 
	function(spell, unit, ignoreIsUsable)
		if ignoreIsUsable == nil then ignoreIsUsable = false end
		if spell == nil then ccR.Error("ccR.Spell.CanAttack - spell is nil") return end
		if unit == nil then unit = "target" end
		--if tonumber(spell) ~= nil then spell = GetSpellInfo(spell) end
		if not ccR.Player.Has.Spell(spell, ignoreIsUsable)
		or ccR.Player.Is.CastingOrChanneling()
		or not HasFullControl()
		then
			return false
		else
			if ccR.Unit.Exists(unit) 
			and ccR.Unit.Health(unit) > 0 
			and (((unit == "player" or unit == "target") and ccR.Spell.Is.Ready(spell)) or ((unit ~= "player" and unit ~= "target") and ccR.Spell.Is.Ready(spell, true))) 
			and (ccR.Spell.Is.InRange(spell, unit) == 1 or ccR.Spell.Is.InRange(spell, unit) == true) 
			then 
				if not ccR.Var("LoS_SpellCanAttack") then 
					return true
				elseif ccR.Unit.Is.InLineOfSight(unit, "player") then 
					return true
				else
					return false
				end
			else
				return false
			end
		end
	end,
	CastTime =
	function(spell)
		if spell == nil then ccR.Error("ccR.Spell.CastTime() - spell is nil") return end
		local name, rank, icon, castingTime, minRange, maxRange, spellID = GetSpellInfo(spell)
		if castingTime == nil then
			ccR.Error("ccR.CastTime(" .. spell .. ") - castingTime is nil")
		else
			return (castingTime/1000)
		end
	end,
	Charges =
	function(spell) 
		--if tonumber(spell) ~= nil then spell = GetSpellInfo(spell) end
		if spell ~= nil then
			local charges = GetSpellCharges(spell) 
			if charges then 
				return charges 
			else
				local _, _, _, count = UnitAura("player", spell) 
				return count 
			end
		else
			return 0
		end
	end,
	CooldownTime = 
	function(spell) 
		if tonumber(spell) ~= nil then spell = GetSpellInfo(spell) end
		if ccR.Player.Has.Spell(spell, true) then
			local start, duration, enable = GetSpellCooldown(spell)
			local name, rank, icon, castTime, minRange, maxRange = GetSpellInfo(spell) 
			if start > 0 and duration > 0 and castTime ~= nil then
				if castTime > 0 then
					return ((start + duration) - GetTime()) - castTime 
				else
					return ((start + duration) - GetTime())
				end
			else
				return 0
			end
		else
			return 999999
		end
	end,
	Is = {
		OnCooldown = 
		function(spell)
			if spell == nil then ccR.Error("ccR.Spell.Is.OnCooldown - spell is nil") return end
			--if tonumber(spell) ~= nil then spell = GetSpellInfo(spell) end
			if ccR.Player.Has.Spell(spell, true) then	
				local substractValue = 0
				if GetCVar("reducedLagTolerance") == 1 then	substractValue = GetCVar("maxSpellStartRecoveryOffset") / 1000 end
				if ccR.Spell.Charges(spell) ~= nil
				and ccR.Spell.Charges(spell) > 0
				then
					return false
				else
					return GetSpellCooldown(spell) - substractValue - (select(4, GetNetStats()) / 1000) > 0	
				end
			else
				return false
			end
		end,
		Usable = 
		function(spell) 
			--if tonumber(spell) ~= nil then spell = GetSpellInfo(spell) end
			local isUsable, notEnoughMana = IsUsableSpell(spell) 
			if isUsable
			and not notEnoughMana
			then
				return true
			else
				return false
			end
		end,
		Ready =
		function(spell, ignoreIsUsable)	
			if ignoreIsUsable == nil then ignoreIsUsable = false end
			--if tonumber(spell) ~= nil then spell = GetSpellInfo(spell) end
			if ccR.Player.Has.Spell(spell, true) then
			 	if (ignoreIsUsable or ignoreIsUsable ~= nil) then
					if not ccR.Spell.Is.OnCooldown(spell) 
					and not ccR.Spell.OnGCD(spell)
					then
						return true
					else
						return false
					end
				else
				 	if ccR.Spell.Is.Usable(spell) then
						if not ccR.Spell.Is.OnCooldown(spell) 
						and not ccR.Spell.OnGCD(spell)
						then
				 			return true
				 		else
				 			return false
				 		end
			 		else
			 			return false
			 		end
				end
			else
				return false
			end
		end,
		InRange = 
		function(spell, unit)
			--if tonumber(spell) ~= nil then spell = GetSpellInfo(spell) end
			if ccR.Unit.Exists(unit)
			and ccR.Player.Has.Spell(spell)
			then
				local inRange = IsSpellInRange(spell, unit);
				if inRange == 1 then
					return true;
				else
					local index, book = fawGetSpellIndex(spell);
					if index then
						inRange = IsSpellInRange(index, book, unit);
						if inRange == 1 then
							return true;
						else
							local unitDistance = ccR.Unit.Distance(unit, "player");
							local unitCombatReach = UnitCombatReach(unit)
							local _, _, _, _, _, _, _, spellMinRange, spellMaxRange = GetSpellInfo(spell);
							local inMinRange = false;
							local inMaxRange = false;
							if spell == SOLAR_BEAM then spellMaxRange = 40
							elseif (spell == DISRUPTING_SHOUT or spell == SHOCKWAVE) then spellMaxRange = 10;
							elseif spell == DRAGON_ROAR or spell == THUNDER_CLAP then
								spellMaxRange = 8;
							end
							if not spellMaxRange or not spellMinRange then return false end
							if type(spellMinRange) == "number" and type(spellMaxRange) == "number" and spellMaxRange > 0 then
							    if unitDistance >= spellMinRange then inMinRange = true end
								if unitDistance <= spellMaxRange + math.floor(unitCombatReach) then inMaxRange = true end
							    if inMinRange and inMaxRange then InRange = true else InRange = false end
							end
						end
					end
				end
				return inRange;
			else
				return false
			end
		end,
	},
	-- Below donated by Wopak
	OnGCD = function(spell)
		local substractValue = 0;
		if GetCVar("reducedLagTolerance") == 1 then	substractValue = GetCVar("maxSpellStartRecoveryOffset") / 1000; end
		if spell == nil then
			return GetSpellCooldown(61304) - substractValue - (select(4, GetNetStats()) / 1000) >= 0;
		else
			return (select(6, ccR.Spell.GetSpellInfo(spell))) - substractValue - (select(4, GetNetStats()) / 1000) >= 0;
		end
		return false;
	end,
	GetSpellInfo = function(spell)
		local spellName, spellID, spellCastTime, spellMinRange, spellMaxRange, spellCooldown, spellNameGlobal, spellIDGlobal, spellIndex, spellBook, spellIsHarmful = nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil;
		if ((type(spell) == "number" or type(spell) == "string")) then
			if type(spell) == "number" then
				spell = (select(1, GetSpellInfo(spell)));
			end
			local spellTab, doLoop = 1, true;
			while spellTab <= 2 and doLoop do
				local _, _, tabOffset, tabNumSpells, _, _ = GetSpellTabInfo(spellTab);
				for i = tabOffset + 1, tabNumSpells + tabOffset do
					thisSpellName = (select(1, GetSpellInfo(i, "spell")));
					if thisSpellName and spell:lower() == thisSpellName:lower() then
						spellName, _, _, spellCastTime, spellMinRange, spellMaxRange, spellID = GetSpellInfo(i, "spell");
						spellIDGlobal = (select(2, GetSpellBookItemInfo(i, "spell")));
						spellNameGlobal = (select(1, GetSpellInfo(spellIDGlobal)));
						spellIndex = i;
						spellBook = "spell"
						spellIsHarmful = IsHarmfulSpell(spellNameGlobal);
						doLoop = false;
						break
					end
				end
				spellTab = spellTab + 1;
			end
			if not spellIndex then
				local numPetSpells = HasPetSpells();
				if numPetSpells then
					for i = 1, numPetSpells do
						thisSpellName = (select(1, GetSpellInfo(i, "pet")));
						if thisSpellName and spell:lower() == thisSpellName:lower() then
							spellName, _, _, spellCastTime, spellMinRange, spellMaxRange, spellID = GetSpellInfo(i, "pet");
							spellIDGlobal = (select(2, GetSpellBookItemInfo(i, "pet")));
							spellNameGlobal = (select(1, GetSpellInfo(spellIDGlobal)));
							spellIndex = i;
							spellBook = "pet"
							spellIsHarmful = IsHarmfulSpell(spellNameGlobal);
							break
						end
					end
				end
			end
			if spellIndex then
				local start ,duration, enable = GetSpellCooldown(spellIDGlobal);
				if start == 0 then
					spellCooldown = 0;
				else
					spellCooldown = duration - (GetTime() - start);
				end
			end
		else
			return nil;
		end
		return spellName, spellID, spellCastTime, spellMinRange, spellMaxRange, spellCooldown, spellNameGlobal, spellIDGlobal, spellIndex, spellBook, spellIsHarmful;
	end,
	-- old --
	IsOnCooldown = function(spell) return ccR.Spell.Is.OnCooldown(spell) end,
	IsUsable = function(spell) return ccR.Spell.Is.Usable(spell) end,
	IsReady = function(spell) return ccR.Spell.Is.Ready(spell) end,
	IsInRange = function(spell, unit) return ccR.Spell.Is.InRange(spell, unit) end,
	CDDuration = function(spell) return ccR.Spell.CooldownTime(spell) end,
	---------
}
setmetatable(ccR.Spell, ccR)

-- Remake
--[[
ccR.Spell = {
	CanAttack = function(spell, unit) end,
	Can = { Attack = function(spell, unit) return ccR.Spell.CanAttack(spell, unit) end,	}
	Cooldown = {
		Duration = function() end,
		Time = function() end,
	}
	Is = {
		In = {
			Range = 
			function(spell, unit)
				if unit == nil then unit = "target" end
				local vDistance = ccR.Unit.Distance(unit, "player") 
				if vDistance >= ccR.Spell.Info.MinRange(spell)
				and vDistance <= ccR.Spell.Info.MaxRange(spell) 
				then
					return true
				else
					return false
				end
			end,
		}
		On = {
			Cooldown = function() end,
			GCD = function() end,
		}
		Ready = function() end,
		Usable = function() end,
	}
	Info = {
		CastTime = function() end,
		Charges = function() end,
		MinRange = function(spell) end,
		MaxRange = function(spell) end,
	}
}
]]